/*
 * Galaxium Messenger
 * Copyright (C) 2007 Ben Motmans <ben.motmans@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

using Anculus.Core;

using Galaxium.Core;
using Galaxium.Protocol.Msn.Soap.BodyParts;

namespace Galaxium.Protocol.Msn
{
	public sealed class MsnAccountCache : AbstractAccountCache
	{
		string _securityTokenDirectory;
		
		public MsnAccountCache (IAccount account, string directory) : base (account, directory)
		{
			_securityTokenDirectory = Path.Combine (Directory, "SecurityTokens");
			
			BaseUtility.CreateDirectoryIfNeeded (_securityTokenDirectory);
			
			_imagesDir = MsnObject.GetCacheDirectory (account as MsnAccount, MsnObjectType.UserDisplay);
		}
		
		public override void SaveAccountImage (string filename)
		{
			new MsnDisplayImage (Account.Session as MsnSession).Filename = filename;
		}
		
		public override void SaveAccountImage ()
		{
		}
		
		public override void GetAccountImage ()
		{
		}
		
		public override void SaveDisplayImage (IContact contact)
		{
		}
		
		public override IDisplayImage GetDisplayImage (IContact contact)
		{
			return contact.DisplayImage as MsnDisplayImage;
		}
		
		internal void SaveSecurityToken (RequestSecurityTokenResponse token)
		{
			FileStream stream = File.OpenWrite (Path.Combine (_securityTokenDirectory, BaseUtility.GetHashedName (token.AppliesTo.EndpointReference.Address)));
			
			XmlSerializer serializer = new XmlSerializer (typeof (RequestSecurityTokenResponse));
			serializer.Serialize (stream, token);
			
			stream.Close ();
		}
		
		internal List<RequestSecurityTokenResponse> GetSecurityTokens ()
		{
			List<RequestSecurityTokenResponse> result = new List<RequestSecurityTokenResponse> ();
			XmlSerializer serializer = new XmlSerializer (typeof (RequestSecurityTokenResponse));
			
			foreach (string filename in System.IO.Directory.GetFiles (_securityTokenDirectory))
			{
				try
				{
					FileStream stream = File.OpenRead (filename);
					RequestSecurityTokenResponse token = serializer.Deserialize (stream) as RequestSecurityTokenResponse;
					stream.Close ();
					
					if (token != null)
						result.Add (token);
				}
				catch (Exception)
				{
					Log.Warn ("Deleting invalid cached security token {0}", Path.GetFileName (filename));
					File.Delete (filename);
				}
			}
			
			return result;
		}
		
		void RemoveOldCached (string name, string time)
		{
			foreach (string filename in System.IO.Directory.GetFiles (Directory, name + "_*"))
			{
				try
				{
					int pos = filename.LastIndexOf ('_');
					string dt = filename.Substring (pos + 1, filename.Length - pos - 1);
					
					if (dt != time)
					{
						Log.Debug ("Removing old cached {0} {1}", name, filename);
						File.Delete (filename);
					}
				}
				catch (Exception ex)
				{
					Log.Error (ex, "Unable to remove cache file {0}", filename);
				}
			}
		}
		
		void SaveSerialized (object obj, string name, string time)
		{
			string filename = Path.Combine (Directory, string.Format ("{0}_{1}", name, time));
			FileStream stream = File.Open (filename, FileMode.Create);
			
			try
			{
				XmlSerializer serializer = new XmlSerializer (obj.GetType ());
				serializer.Serialize (stream, obj);
				
				RemoveOldCached (name, time);
			}
			catch (Exception ex)
			{
				Log.Error (ex, "Unable to serialize {0} to {1}", name, filename);
			}
			finally
			{
				stream.Close ();
			}
		}
		
		object LoadSerialized (Type type, string name, string time)
		{
			string filename = Path.Combine (Directory, string.Format ("{0}_{1}", name, time));
			
			if (!File.Exists (filename))
			{
				Log.Debug ("File {0} does not exist", filename);
				return null;
			}
			
			FileStream stream = File.OpenRead (filename);
			object ret = null;
			
			try
			{
				XmlSerializer serializer = new XmlSerializer (type);
				ret = serializer.Deserialize (stream);
				
				Log.Debug ("Successfully deserialized cached {0} {1}", name, filename);
			}
			catch (Exception ex)
			{
				Log.Warn (ex, "Unable to deserialize cached {0} {1}", name, filename);
			}
			finally
			{
				stream.Close ();
			}
			
			return ret;
		}
		
		public void SaveCachedMembership (FindMembershipResult result, string time)
		{
			SaveSerialized (result, "membership", time);
		}
		
		public bool LoadCachedMembership (string time)
		{
			FindMembershipResult result = LoadSerialized (typeof (FindMembershipResult), "membership", time) as FindMembershipResult;
			
			if (result != null)
			{
				(Account.Session as MsnSession).UpdateMemberships (result);
				return true;
			}
			
			return false;
		}
		
		public void SaveCachedAddressBook (ABFindAllResult result, string time)
		{
			SaveSerialized (result, "addressbook", time);
		}
		
		public bool LoadCachedAddressBook (string time)
		{
			ABFindAllResult result = LoadSerialized (typeof (ABFindAllResult), "addressbook", time) as ABFindAllResult;
			
			if (result != null)
			{
				(Account.Session as MsnSession).UpdateAddressBook (result);
				return true;
			}
			
			return false;
		}
	}
}
